""" Pushbutton debouncer module

    Provides a configurable pushbutton debouncer
"""

from util_cores.common_constants import ACTIVE_HIGH, ACTIVE_LOW, INACTIVE_HIGH, INACTIVE_LOW
from myhdl import always, Signal, intbv, instance, instances, bin

SAMPLING_FREQUENCY = 100  #Hz 
NUMBER_OF_EQUAL_SAMPLES = 4

def pb_debouncer(
                 Enable
                 ,Clk
                 ,PbIn
                 ,PbOut
                 ,clkHz
                 ,activeLevel=ACTIVE_LOW
                 ,samplingFrequency=SAMPLING_FREQUENCY
                 ,numberOfEqualSamples=NUMBER_OF_EQUAL_SAMPLES
                ):
    """ Pushbutton debouncer hardware design

        The design samples the input signal at a frequency close to SAMPLING_FREQUENCY and sets the
        output signal once at least 4 equal samples have been recorded. The input clock frequency is
        internaly scaled down.

        External interface:
        PbIn -- pushbutton signal in
        Enable -- active high enable input signal, if 0 the PbOut is forced to its inactive level
        PbOut -- the debounced pushbutton output
        Clk -- clock signal input, should be at least SAMPLING_FREQUENCY Hz
       
        Parameters:
        clkHz -- the Clk frequency in Hz
        activeLevel -- the pushbutton active level, ACTIVE_HIGH or ACTIVE_LOW (default: ACTIVE_LOW)
        samplingFreq -- how often we sample PbIn (default: SAMPLING_FREQUNCY Hz)
        numberOfEqualSamples -- after this many samples the input is considered stable (default:
        NUMBER_OF_EQUAL_SAMPLES)

    """
    SAMPLE_BUFFER_MAX = intbv(2**numberOfEqualSamples-1)[numberOfEqualSamples:]

    if ACTIVE_LOW == activeLevel:
        SAMPLE_BUFFER_INIT_STATE = intbv(SAMPLE_BUFFER_MAX)[numberOfEqualSamples:]
    else:
        SAMPLE_BUFFER_INIT_STATE = intbv(0)[numberOfEqualSamples:]

    @instance
    def debouncer():
        sampleBuffer = intbv(SAMPLE_BUFFER_INIT_STATE)[numberOfEqualSamples:]
        SAMPLE_BUFFER_MSB = len(sampleBuffer)
        STABLE_HIGH = intbv(SAMPLE_BUFFER_MAX)[numberOfEqualSamples:]
        ACTIVE_LEVEL = bool(activeLevel)

        while True:
            yield Clk.posedge, Enable.negedge

            if Enable == INACTIVE_LOW:
                PbOut.next = not ACTIVE_LEVEL
            else:
                sampleBuffer[SAMPLE_BUFFER_MSB:1] = sampleBuffer[SAMPLE_BUFFER_MSB-1:]  # shift left
                sampleBuffer[0] = PbIn

                if __debug__:
                    print "sampleBuffer = %s" % bin(sampleBuffer, len(sampleBuffer))

                if not sampleBuffer:
                    PbOut.next = 0
                elif sampleBuffer == STABLE_HIGH:
                    PbOut.next = 1

    return instances()

